﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace Chapter5Lesson1Practice
{
	class Program
	{
		private const String XDocumentTestFilePath = "XDocumentTest.xml";

		static void Main(string[] args)
		{
			#region Lesson1 practice

			//var xmlDoc = new XmlDocument();
			//xmlDoc.Load(GetFilePath("XmlDocumentTest.xml"));
			//XmlDocumentPractice();
			//ParseXmlDocument(xmlDocument);
			//PerformXpathSearch(xmlDocument);
			//PerformGetElementMethod(xmlDoc);
			//PerformSelectNodesMethod(xmlDoc);
			//PerformParseWithXmlReader(xmlDoc);

			#endregion

			#region XDocument Practices

			//ParseXDocument();
			//ParseXDocument2();
			//QueryXDocument2();
			//JoinsWithXDocument();
			//UsingNamespacesWithXDocumemnt();
			
			#endregion

			#region Lesson3 practice

			//TransformXmlIntoObjects(GetFilePath("XDocumentTest.xml"));
			//TransformXmlIntoText(GetFilePath(XDocumentTestFilePath));
			TransformXmlIntoXml(GetFilePath(XDocumentTestFilePath));

			#endregion

			Console.ReadLine();
		}

		#region Worker methods

		#region XmlDocument & XmlReader

		private static void XmlDocumentPractice()
		{
			var xmlDoc = new XmlDocument();
			int childCounter;

			xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null));

			XmlElement xmlElement = xmlDoc.CreateElement("MyRoot");
			xmlDoc.AppendChild(xmlElement);

			for(childCounter = 1; childCounter <= 4; childCounter++)
			{
				XmlElement childElement = xmlDoc.CreateElement("MyChild");
				XmlAttribute childAttr = xmlDoc.CreateAttribute("ID");
				childAttr.Value = childCounter.ToString();
				childElement.Attributes.Append(childAttr);

				//Append element into the root element
				xmlElement.AppendChild(childElement);
				int grandChildCounter;
				for (grandChildCounter = 1; grandChildCounter <= 3; grandChildCounter++)
				{
					//Create grandchildren
					childElement.AppendChild(xmlDoc.CreateElement("MyGrandChild"));
				}
			}

			xmlDoc.Save(GetFilePath("XmlDocumentTest.xml"));
			Console.WriteLine("XmlDocumentTest.xml Created\r\n");
		}

		private static void ParseXmlDocument(XmlDocument xmlDocument)
		{
			RecurseNodes(xmlDocument.DocumentElement);
		}

		private static void PerformXpathSearch(XmlDocument xmlDocument)
		{
			var node = xmlDocument.SelectSingleNode("//MyChild[@ID='3']");
			RecurseNodes(node);
		}

		private static void PerformGetElementMethod(XmlDocument xmlDocument)
		{
			var elements = xmlDocument.GetElementsByTagName("MyGrandChild");

			var sb = new StringBuilder();

			foreach (XmlNode node in elements)
			{
				RecurseNodes(node, 0, sb);
			}
			Console.WriteLine(sb);
		}

		private static void PerformSelectNodesMethod(XmlDocument xmlDocument)
		{
			var elements = xmlDocument.SelectNodes("//MyGrandChild");
			var sb = new StringBuilder();

			foreach (XmlNode xmlNode in elements)
			{
				RecurseNodes(xmlNode, 0, sb);
			}

			Console.WriteLine(sb);
		}

		private static void PerformParseWithXmlReader(XmlDocument xmlDocument)
		{
			var sb = new StringBuilder();
			var xmlReader = new XmlTextReader(GetFilePath("XmlDocumentTest.xml"));

			while (xmlReader.Read())
			{
				switch (xmlReader.NodeType)
				{
					case XmlNodeType.XmlDeclaration:
					case XmlNodeType.Element:
					case XmlNodeType.Comment:
						{
							sb.AppendFormat("{0}: {1} = {2}", xmlReader.NodeType, xmlReader.Name, xmlReader.Value);
							sb.AppendLine();
							break;
						}
					case XmlNodeType.Text:
						{
							sb.AppendFormat(" - Value: {0}", xmlReader.Value);
							break;
						}
				}

				if(xmlReader.HasAttributes)
				{
					while (xmlReader.MoveToNextAttribute())
					{
						sb.AppendFormat(" - Attribute: {0} - {1}", xmlReader.Name, xmlReader.Value);
						sb.AppendLine();
					}
				}
			}
			xmlReader.Close();
			Console.WriteLine(sb);
		}

		#endregion

		#region XDocument & Linq2Xml

		private static void ParseXDocument()
		{
			const string xml = @"<CustomersOrders>
						<Customer CustomerID='ALFKI' CompanyName='Alfreds Futterkiste'>
						<Order OrderID='10643' Freight='29.4600' />
						<Order OrderID='10692' Freight='61.0200' />
						<Order OrderID='10702' Freight='23.9400' />
						<Order OrderID='10835' Freight='69.5300' />
						<Order OrderID='10952' Freight='40.4200' />
						<Order OrderID='11011' Freight='1.2100' />
						</Customer>
						<Customer CustomerID='ANATR' CompanyName='Ana Trujillo'>
						<Order OrderID='10308' Freight='1.6100' />
						<Order OrderID='10625' Freight='43.9000' />
						<Order OrderID='10759' Freight='11.9900' />
						<Order OrderID='10926' Freight='39.9200' />
						</Customer>
						<Customer CustomerID='ANTON' CompanyName='Antonio Moreno'>
						<Order OrderID='10365' Freight='22.0000' />
						<Order OrderID='10507' Freight='47.4500' />
						<Order OrderID='10535' Freight='15.6400' />
						<Order OrderID='10573' Freight='84.8400' />
						<Order OrderID='10677' Freight='4.0300' />
						<Order OrderID='10682' Freight='36.1300' />
						<Order OrderID='10856' Freight='58.4300' />
						</Customer>
						<Customer CustomerID='AROUT' CompanyName='Around the Horn'>
						<Order OrderID='10355' Freight='41.9500' />
						<Order OrderID='10383' Freight='34.2400' />
						<Order OrderID='10453' Freight='25.3600' />
						<Order OrderID='10558' Freight='72.9700' />
						<Order OrderID='10707' Freight='21.7400' />
						<Order OrderID='10741' Freight='10.9600' />
						<Order OrderID='10743' Freight='23.7200' />
						<Order OrderID='10768' Freight='146.3200' />
						<Order OrderID='10793' Freight='4.5200' />
						<Order OrderID='10864' Freight='3.0400' />
						<Order OrderID='10920' Freight='29.6100' />
						<Order OrderID='10953' Freight='23.7200' />
						<Order OrderID='11016' Freight='33.8000' />
						</Customer>
						</CustomersOrders>
						";
			var xdocument = XDocument.Parse(xml);

			var path = GetFilePath("XDocumentText.xml");

			xdocument.Save(path);
			Console.WriteLine("File {0} has been saved.", path);
		}

		private static void ParseXDocument2()
		{
			var doc = new XDocument(
				new XElement("CustomersOrders",
				             new XElement("Customer",
				                          new XAttribute("CustomerID", "ALFKI"),
				                          new XAttribute("CompanyName", "Alfreds Futterkiste"),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10643"),
				                                       new XAttribute("Freight", "29.4600")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10692"),
				                                       new XAttribute("Freight", "61.0200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10702"),
				                                       new XAttribute("Freight", "23.9400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10835"),
				                                       new XAttribute("Freight", "69.5300")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10952"),
				                                       new XAttribute("Freight", "40.4200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "11011"),
				                                       new XAttribute("Freight", "1.2100"))),
				             new XElement("Customer",
				                          new XAttribute("CustomerID", "ANATR"),
				                          new XAttribute("CompanyName", "Ana Trujillo"),
				                          new XElement("Order", new XAttribute("OrderID", "10308"),
				                                       new XAttribute("Freight", "1.6100")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10625"),
				                                       new XAttribute("Freight", "43.9000")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10759"),
				                                       new XAttribute("Freight", "11.9900")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10926"),
				                                       new XAttribute("Freight", "39.9200"))),
				             new XElement("Customer",
				                          new XAttribute("CustomerID", "ANTON"),
				                          new XAttribute("CompanyName", "Antonio Moreno"),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10365"),
				                                       new XAttribute("Freight", "22.0000")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10507"),
				                                       new XAttribute("Freight", "47.4500")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10535"),
				                                       new XAttribute("Freight", "15.6400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10573"),
				                                       new XAttribute("Freight", "84.8400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10677"),
				                                       new XAttribute("Freight", "4.0300")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10682"),
				                                       new XAttribute("Freight", "36.1300")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10856"),
				                                       new XAttribute("Freight", "58.5300"))),
				             new XElement("Customer",
				                          new XAttribute("CustomerID", "AROUT"),
				                          new XAttribute("CompanyName", "Around the Horn"),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10355"),
				                                       new XAttribute("Freight", "41.9500")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10383"),
				                                       new XAttribute("Freight", "34.2400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10453"),
				                                       new XAttribute("Freight", "25.3600")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10558"),
				                                       new XAttribute("Freight", "72.9700")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10707"),
				                                       new XAttribute("Freight", "21.7400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10741"), new XAttribute("Freight", "10.9600")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10743"),
				                                       new XAttribute("Freight", "23.7200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10768"),
				                                       new XAttribute("Freight", "146.3200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10793"),
				                                       new XAttribute("Freight", "4.5200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10864"),
				                                       new XAttribute("Freight", "3.0400")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10920"),
				                                       new XAttribute("Freight", "29.6100")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "10953"),
				                                       new XAttribute("Freight", "23.7200")),
				                          new XElement("Order",
				                                       new XAttribute("OrderID", "11016"),
				                                       new XAttribute("Freight", "33.8000")))));

			var path = GetFilePath("XDocumentTest2.xml");
			Console.WriteLine("Document {0} saved successfully", path);
			doc.Save(path);
		}

		private static void QueryXDocument()
		{
			var doc = XDocument.Load(GetFilePath("XDocumentTest.xml"));

			var descendants = doc.Descendants("Order");

			var descendantsWithConcreteOrderId = descendants.Where(o => String.Equals(o.Attribute("OrderID").Value, "10677"));

			var node =
				descendantsWithConcreteOrderId.Select(
					o =>
					new
						{
							CustomerId = o.Parent.Attribute("CustomerID"),
							OrderId = (Int32) o.Attribute("OrderID"),
							Freight = (Decimal) o.Attribute("Freight")
						}).FirstOrDefault();

			Console.WriteLine("Order with Id {0} for customer with Id {1} with freight {2}.", node.OrderId, node.CustomerId, node.Freight);
		}

		private static void QueryXDocument2()
		{
			var doc = XDocument.Load(GetFilePath("XDocumentTest.xml"));
			var result = from customer in doc.Descendants("Customer")
			             select new
			                    	{
			                    		CustomerId = customer.Attribute("CustomerID").ToString(),
										TotalFreight = customer.Descendants("Order").Sum(c => (Decimal)c.Attribute("Freight"))
			                    	};
			foreach (var customer in result)
			{
				Console.WriteLine("Customer {0} has this amount of total freight {1}.", customer.CustomerId, customer.TotalFreight);
			}
		}

		private static void JoinsWithXDocument()
		{
			var ordersArray = new[] {"10707", "10835", "10953"};

			var doc = XDocument.Load(GetFilePath("XDocumentTest.xml"));

			var result = from order in doc.Descendants("Order")
			             join o in ordersArray on (String) order.Attribute("OrderID") equals o
			             select new
			                    	{
										OrderId = o,
										CustomerId = (String)order.Parent.Attribute("CustomerID"),
										Freight = (Decimal)order.Attribute("Freight")
			                    	};

			foreach (var joinedShit in result)
			{
				Console.WriteLine("OrderId with Id - {0} and Freight - {1} for Customer {2}", joinedShit.OrderId, joinedShit.Freight, joinedShit.CustomerId);
			}
		}

		private static void UsingNamespacesWithXDocumemnt()
		{
			const string xml = @"<Root xmlns:aw='http://www.adventure-works.com' xmlns='http://www.xyz.com'>
					<Child>1</Child>
					<aw:Child>2</aw:Child>
					<Child>3</Child>
					<aw:Child>4</aw:Child>
					<Child>5</Child>
					<aw:Child>6</aw:Child>
				</Root>";

			var doc = XDocument.Parse(xml);

			var children = from c in doc.Descendants("Child")
			               select c;

			if (!children.Any())
				Console.WriteLine("No elements without namespace");
			else
			{
				foreach (var xElement in children)
				{
					Console.WriteLine(xElement.Value);
				}
			}

			var aw = XNamespace.Get("http://www.adventure-works.com");

			var childrenWithAwNamespace = from c in doc.Descendants(aw + "Child")
			                              select c;
			Console.WriteLine("Quering for elements with namespace {0}", aw);

			foreach (var xElement in childrenWithAwNamespace)
			{
				Console.WriteLine((String)xElement);
			}

			var defaultNs = XNamespace.Get("http://www.xyz.com");
			Console.WriteLine("Quering for default namespace - {0}", defaultNs);

			var childrenWithDefaultNamespace = from c in doc.Descendants(defaultNs + "Child")
			                                   select c;

			foreach (var xElement in childrenWithDefaultNamespace)
			{
				Console.WriteLine((String)xElement);
			}
		}

		#endregion

		#region Lesson3 practic methods

		private static void TransformXmlIntoObjects(String filePath)
		{
			var doc = XDocument.Load(filePath);

			var customersOrders = from c in doc.Descendants("Customer")
			                      select new Customer
			                             	{
												CustomerId = (String)c.Attribute("CustomerID"),
												Name = (String)c.Attribute("CompanyName"),
												OrdersList = (from o in c.Descendants("Order")
															 select new Order
															        	{
															        		OrderId = (Int32)o.Attribute("OrderID"),
																			Freight = (Decimal)o.Attribute("Freight")
															        	}).ToList()
			                             	};

			foreach (var customersOrder in customersOrders)
			{
				Console.WriteLine("Customer with Id {0} and name {1} has list of following orders below:", customersOrder.CustomerId, customersOrder.Name);
				foreach (var order in customersOrder.OrdersList)
				{
					Console.WriteLine("Order with ID - {0} and Freight {1}", order.OrderId, order.Freight);
				}
			}
		}

		private static void TransformXmlIntoText(String filePath)
		{
			var doc = XDocument.Load(filePath);

			var customersOrders = from c in doc.Descendants("Customer")
			                      select new 
			                             	{
												CustomerInfo = String.Format("Id: {0} CompanyName: {1}", (String)c.Attribute("CustomerID"), (String)c.Attribute("CompanyName")),
												OrderInfo = from o in c.Elements("Order")
															select String.Format("OrderId: {0} Freight: {1}", o.Attribute("OrderID"), o.Attribute("Freight"))
			                             	};
			foreach (var customersOrder in customersOrders)
			{
				Console.WriteLine(customersOrder.CustomerInfo);
				foreach (var order in customersOrder.OrderInfo)
				{
					Console.WriteLine(order);
				}
				Console.WriteLine();
			}
		}

		private static void TransformXmlIntoXml(String filePath)
		{
			var doc = XDocument.Load(filePath);

			var newXml = new XDocument(new XElement("root", from o in doc.Descendants("Order")
			                                                select new XElement("Order",
			                                                                    new XAttribute("CustomerID",
			                                                                                   o.Parent.Attribute("CustomerID").
			                                                                                   	Value),
			                                                                    new XAttribute("CustomerName",
			                                                                                   o.Parent.Attribute("CompanyName").
			                                                                                   	Value),
			                                                                    new XAttribute("OrderID",
			                                                                                   o.Attribute("OrderID").Value),
			                                                                    new XAttribute("OrderFreight",
			                                                                                   o.Attribute("Freight").Value))));
			Console.WriteLine(newXml);
		}

		#endregion

		#endregion

		#region Helper methods

		#region XmlDocument & XmlReader

		private static void RecurseNodes(XmlNode xmlNode)
		{
			var sb = new StringBuilder();
			RecurseNodes(xmlNode, 0, sb);
			Console.WriteLine(sb);
		}

		private static void RecurseNodes(XmlNode xmlNode, Int32 level, StringBuilder sb)
		{
			sb.AppendFormat("{0,2} Type:{1,-9} Name:{2,-13} Attr:", level, xmlNode.NodeType, xmlNode.Name);
			foreach (XmlAttribute attr in xmlNode.Attributes)
			{
				sb.AppendFormat("{0}={1} ", attr.Name, attr.Value);
			}
			sb.AppendLine();
			foreach (XmlNode n in xmlNode.ChildNodes)
			{
				RecurseNodes(n, level + 1, sb);
			}
		}

		private static String GetFilePath(String fileName)
		{
			return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName);
		}

		#endregion

		#region XDocument & Linq2Xml


		#endregion

		#endregion
	}
}
